package org.neptune.service.config;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.neptune.common.constant.CharacterConstant;
import org.neptune.common.constant.NeptuneConstant;
import org.neptune.service.redis.RedisRepository;
import org.neptune.service.shiro.CustomRealm;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.filter.DelegatingFilterProxy;

import com.google.common.base.Splitter;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

/**
 * @author lwl
 * @date 2019/8/15 11:22
 */
@Configuration
public class ShiroConfig {

	@Bean(name = "customRealm")
	public Realm realm(RedisRepository redisRepository, NeptuneConfig neptuneConfig) {
		CustomRealm shiroRealm = new CustomRealm();
		shiroRealm.setCacheManager(redisCacheManager(redisRepository, neptuneConfig));
		shiroRealm.setCachingEnabled(true);
		// 设置认证密码算法及迭代复杂度
		// shiroRealm.setCredentialsMatcher(credentialsMatcher());
		// 认证
		shiroRealm.setAuthenticationCachingEnabled(false);
		// 授权
		shiroRealm.setAuthorizationCachingEnabled(false);
		return shiroRealm;

	}

	/**
	 * realm的认证算法
	 * 
	 * @return
	 */
	@Bean(name = "hashedCredentialsMatcher")
	public HashedCredentialsMatcher credentialsMatcher() {
		HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("md5");
		credentialsMatcher.setHashIterations(2);
		credentialsMatcher.setStoredCredentialsHexEncoded(true);
		return credentialsMatcher;
	}

	@Bean
	public ShiroFilterChainDefinition shiroFilterChainDefinition() {
		DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
		// 不需要在此处配置权限页面,因为下面的ShiroFilterFactoryBean已经配置过,但是此处必须存在,因为shiro-spring-boot-web-starter或查找此Bean,没有会报错
		return chain;
	}

	@Bean
	public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
		/**
		 * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
		 * 在@Controller注解的类的方法中加入@RequiresRole注解，会导致该方法无法映射请求，导致返回404。 加入这项配置能解决这个bug
		 */
		creator.setUsePrefix(true);
		return creator;
	}

	/**
	 * FilterRegistrationBean
	 */
	@Bean
	public FilterRegistrationBean<Filter> filterRegistrationBean() {
		FilterRegistrationBean<Filter> filterRegistration = new FilterRegistrationBean<Filter>();
		filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
		filterRegistration.setEnabled(true);
		filterRegistration.addUrlPatterns("/*");
		filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
		return filterRegistration;
	}

	/**
	 * 过滤器及映射路径的配置
	 */
	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
			RedisRepository redisRepository, @Value("${neptune.shiro.authc}") String authcConfig,
			@Value("${neptune.shiro.anon}") String anonConfig) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		shiroFilterFactoryBean.setLoginUrl("/login");
		shiroFilterFactoryBean.setSuccessUrl("/index.html");
		shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");

		// filter
		// Map<String, Filter> filters = Maps.newLinkedHashMap();
		// filters.put("perms", urlPermissionsFilter());
		// filters.put("logout", new MySignOutFilter(redisTemplate));
		// shiroFilterFactoryBean.setFilters(filters);

		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		List<String> anons = Splitter.on(CharacterConstant.COMMA_EN).splitToList(anonConfig);
		List<String> authcs = Splitter.on(CharacterConstant.COMMA_EN).splitToList(authcConfig);
		for (String anon : anons){
			filterChainDefinitionMap.put(anon, "anon");
		}
		for (String authc : authcs){
			filterChainDefinitionMap.put(authc, "authc");
		}
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;

	}

	/**
	 * RedisSessionDAO
	 */
	@Bean
	public MyRedisSessionDao myRedisSessionDAO(RedisRepository redisRepository,
			NeptuneConfig neptuneConfig) {
		MyRedisSessionDao redisSessionDAO = new MyRedisSessionDao(redisRepository,
				neptuneConfig.loginSessionCachetime);
		return redisSessionDAO;
	}

	/**
	 * 安全管理模块，所有的manager在此配置
	 * 
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public DefaultWebSecurityManager securityManager(RedisRepository redisRepository,
			NeptuneConfig neptuneConfig) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

		// 自定义realm
		securityManager.setRealm(realm(redisRepository, neptuneConfig));

		// 自定义session管理 使用redis
		securityManager.setSessionManager(sessionManager(redisRepository, neptuneConfig));

		// //自定义缓存实现 使用redis
		securityManager.setCacheManager(redisCacheManager(redisRepository, neptuneConfig));
		// 注入记住我管理器;
		// securityManager.setRememberMeManager(rememberMeManager());

		return securityManager;
	}

	/**
	 * 缓存管理器的配置
	 * 
	 * @param redisTemplate
	 * @return
	 */
	@Bean(name = "shiroRedisCacheManager")
	public ShiroRedisCacheManager redisCacheManager(RedisRepository redisRepository,
			NeptuneConfig neptuneConfig) throws CacheException {
		ShiroRedisCacheManager redisCacheManager = new ShiroRedisCacheManager(redisRepository,
				neptuneConfig);
		// name是key的前缀，可以设置任何值，无影响，可以设置带项目特色的值
		redisCacheManager.createCache(NeptuneConstant.APP_NAME);
		return redisCacheManager;
	}

	/**
	 * 配置sessionmanager，由redis存储数据
	 */
	@Bean(name = "sessionManager")
	@DependsOn(value = "lifecycleBeanPostProcessor")
	public DefaultWebSessionManager sessionManager(RedisRepository redisRepository,
			NeptuneConfig neptuneConfig) {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();

		// 这个name的作用也不大，只是有特色的cookie的名称。
		// redisSessionDao.setSessionIdGenerator(sessionIdGenerator("starrkCookie"));

		sessionManager.setSessionDAO(myRedisSessionDAO(redisRepository, neptuneConfig));
		sessionManager.setDeleteInvalidSessions(true);
		SimpleCookie cookie = new SimpleCookie();
		cookie.setName("starrkCookie");
		sessionManager.setSessionIdCookie(cookie);
		sessionManager.setSessionIdCookieEnabled(true);
		return sessionManager;
	}

	/**
	 * shiro管理生命周期的东西
	 * 
	 * @return
	 */
	@Bean(name = "lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	/**
	 * shiro 界面标签
	 * 
	 * @return
	 */
	@Bean
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}

	// /**
	// * 解决spring-boot Whitelabel Error Page
	// * @return
	// */
	// @Bean
	// public EmbeddedServletContainerCustomizer containerCustomizer() {
	//
	// return new EmbeddedServletContainerCustomizer() {
	// @Override
	// public void customize(ConfigurableEmbeddedServletContainer container) {
	//
	// ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED,
	// "/unauthorized.html");
	// ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND,
	// "/404.html");
	// ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,
	// "/500.html");
	//
	// container.addErrorPages(error401Page, error404Page, error500Page);
	// }
	// };
	// }

}
